﻿namespace MEF_Test.Bricks.Subtitles
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Reflection;

    using MEF_Test.Contracts;

    /// <summary>
    /// The class responsible to import and manage all the subtitles providers.
    /// </summary>
    /// <remarks>The subtitles providers are imported throug Constructor Injection (with the help of MEF's ImportingConstructor)</remarks>
    [Export(typeof(ISubtitlesProvidersManager))]
    public class SubtitlesProvidersManager : INotifyPropertyChanged, ISubtitlesProvidersManager
    {
        #region Constants and Fields

        /// <summary>
        /// The exported subtitles providers
        /// </summary>
        private readonly IEnumerable<ISubtitlesProvider> _subtitlesProviders;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SubtitlesProvidersManager"/> class.
        /// </summary>
        /// <param name="subtitlesProviders">The subtitles exported providers.</param>
        [ImportingConstructor]
        public SubtitlesProvidersManager([ImportMany(typeof(ISubtitlesProvider))] IEnumerable<ISubtitlesProvider> subtitlesProviders)
        {
            _subtitlesProviders = subtitlesProviders;
        }

        #endregion

        #region Events

        

        /// <summary>
        /// Occurs when results for the asynchronous subtitles request are received].
        /// </summary>
        public event AsyncSubtitlesResultsReceivedEventHandler AsyncSubtitlesResultsReceived;

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Implemented Interfaces

        #region ISubtitlesProvidersManager

        /// <summary>
        /// Gets the provider.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetProvider<T>()
        {
            var v = _subtitlesProviders.Where(a => true);
            return (T)v.ElementAt(0);
        }


        ///// <summary>
        ///// Gets the subtitles.
        ///// </summary>
        ///// <param name="node">The node for which to find subtitles.</param>
        ///// <returns>A list of all the subtitles found for each subtitles provider.</returns>
        //public List<SubtitlesProviderResults> GetSubtitles(IBrowserNodeViewModel node)
        //{
        //    var responses = new List<SubtitlesProviderResults>();
        //    foreach (var subtitlesProvider in _subtitlesProviders)
        //    {
        //        responses.Add(subtitlesProvider.GetSubtitles(node));
        //    }

        //    return responses;
        //}

        /// <summary>
        /// Gets the subtitles asynchronously.
        /// </summary>
        /// <param name="node">The node for which to find subtitles.</param>
        /// <returns>
        /// A non-observable enumerable of observable <see cref="SubtitlesProviderResults"/> containing infos about the search results for a specific subtitles provider.
        /// </returns>
        /// <remarks>The returned value might not be complete by the time they are returned, but the members will be updated asynchronously in the background to reflect the changes if the subtitles provider is slow, such as a remote webservice.</remarks>
        public IEnumerable<SubtitlesProviderResults> GetSubtitlesAsync(IBrowserNodeViewModel node)
        {
            // TODO : Shouldn't we use Interfaces instead of concrete implementation of the SubtitlesProviderResults ?
            var returnValue = new List<SubtitlesProviderResults>(_subtitlesProviders.Count());
            foreach (var subtitlesProvider in _subtitlesProviders)
            {
                // To avoid accessing modified closure.
                var provider = subtitlesProvider;

                SubtitlesProviderResults subtitlesProviderResults = subtitlesProvider.GetSubtitlesAsync(node);
                subtitlesProviderResults.ProviderName = provider.GetProviderName();                

                returnValue.Add(subtitlesProviderResults);
            }

            return returnValue;
        }

        #endregion

        #endregion

        #region Methods

        ///// <summary>
        ///// Called then the subtitles retrival ends.
        ///// </summary>
        ///// <param name="sender">The sender.</param>
        ///// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        //private void endGetSubtitles(object sender, RunWorkerCompletedEventArgs e)
        //{
        //    SubtitlesProviderResults subtitlesProviderResults = null;

        //    try
        //    {
        //        subtitlesProviderResults = e.Result as SubtitlesProviderResults;
        //    }
        //    catch (TargetInvocationException)
        //    {
        //        // TODO Warn the user somehow that something went wrong while loading the subs list.
        //        subtitlesProviderResults = new SubtitlesProviderResults
        //            {
        //                ProviderName = this._backgroundWorkers[(BackgroundWorker)sender].GetProviderName(),
        //                Subtitles = new List<ISubtitleViewModel>()
        //            };
        //    }
        //    finally
        //    {
        //        var eventArgs = new AsyncSubtitlesResultsReceivedEventArgs
        //            {
        //                Results = subtitlesProviderResults
        //            };
        //        onAsyncSubtitlesResultsReceived(this, eventArgs);
        //    }
        //}

        /// <summary>
        /// Called when the async subtitles query's response is received.
        /// </summary>
        /// <param name="subtitlesProvidersManager">The subtitles providers manager.</param>
        /// <param name="eventArgs">The <see cref="MEF_Test.Bricks.Subtitles.AsyncSubtitlesResultsReceivedEventArgs"/> instance containing the event data.</param>
        private void onAsyncSubtitlesResultsReceived(ISubtitlesProvidersManager subtitlesProvidersManager, AsyncSubtitlesResultsReceivedEventArgs eventArgs)
        {
            if (AsyncSubtitlesResultsReceived != null)
            {
                AsyncSubtitlesResultsReceived(this, eventArgs);
            }
        }

        #endregion
    }
}
